package com.riuzzang.android.callmebaby.util.mp3;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.security.InvalidParameterException;

import android.media.AudioFormat;
import android.media.AudioRecord;
import android.media.MediaRecorder;
import android.os.Handler;

/**
 * 마이크에서 가져온 음성을 MP3에 저장
 * 다른 스레드에서 마이크에서 녹음, MP3로 변환
 */
public class RecMicToMp3 {

	static {
		System.loadLibrary("mp3lame");
	}

	/**
	 * MP3 파일을 저장할 파일 경로
	 */
	private String mFilePath;

	/**
	 * 샘플링 속도
	 */
	private int mSampleRate;

	/**
	 * 녹음 중 여부
	 */
	private boolean mIsRecording = false;

	/**
	 * 녹음 상태 변화를 알려주는 핸들러
	 * 
	 * @see RecMicToMp3#MSG_REC_STARTED
	 * @see RecMicToMp3#MSG_REC_STOPPED
	 * @see RecMicToMp3#MSG_ERROR_GET_MIN_BUFFERSIZE
	 * @see RecMicToMp3#MSG_ERROR_CREATE_FILE
	 * @see RecMicToMp3#MSG_ERROR_REC_START
	 * @see RecMicToMp3#MSG_ERROR_AUDIO_RECORD
	 * @see RecMicToMp3#MSG_ERROR_AUDIO_ENCODE
	 * @see RecMicToMp3#MSG_ERROR_WRITE_FILE
	 * @see RecMicToMp3#MSG_ERROR_CLOSE_FILE
	 */
	private Handler mHandler;

	/**
	 * 녹음을 시작했다
	 */
	public static final int MSG_REC_STARTED = 0;

	/**
	 * 녹음이 끝난
	 */
	public static final int MSG_REC_STOPPED = 1;

	/**
	 * 버퍼 크기가 가져올 수 없습니다. 샘플링 레이트 등의 설정을 단말기가 지원하지 않을 수있다.
	 */
	public static final int MSG_ERROR_GET_MIN_BUFFERSIZE = 2;

	/**
	 * 파일을 생성 할 수없는
	 */
	public static final int MSG_ERROR_CREATE_FILE = 3;

	/**
	 * 녹음 시작 실패
	 */
	public static final int MSG_ERROR_REC_START = 4;
	
	/**
	 * 녹음 수 없다. 녹음 시작 후 만 발행한다.
	 */
	public static final int MSG_ERROR_AUDIO_RECORD = 5;

	/**
	 * 인코딩에 실패했다. 녹음 시작 후 만 발행한다.
	 */
	public static final int MSG_ERROR_AUDIO_ENCODE = 6;

	/**
	 * 파일 내보내기에 실패했다. 녹음 시작 후 만 발행한다.
	 */
	public static final int MSG_ERROR_WRITE_FILE = 7;

	/**
	 * 파일 닫기에 실패했다. 녹음 시작 후 만 발행한다.
	 */
	public static final int MSG_ERROR_CLOSE_FILE = 8;

	/**
	 * 생성자
	 * 
	 * @param filePath
	 *            保存するファイルパス
	 * @param sampleRate
	 *            録音するサンプリングレート（Hz）
	 */
	public RecMicToMp3(String filePath, int sampleRate) {
		if (sampleRate <= 0) {
			throw new InvalidParameterException("Invalid sample rate specified.");
		}
		this.mFilePath = filePath;
		this.mSampleRate = sampleRate;
	}

	/**
	 * 録音を開始する
	 */
	public void start() {
		// 녹음의 경우는 아무것도하지
		if (mIsRecording) {
			return;
		}

		// 녹음을 다른 스레드에서 시작
		new Thread() {
			@Override
			public void run() {
				android.os.Process.setThreadPriority(android.os.Process.THREAD_PRIORITY_URGENT_AUDIO);
				
				// 최소 버퍼 크기
				final int minBufferSize = AudioRecord.getMinBufferSize(mSampleRate, AudioFormat.CHANNEL_IN_MONO, AudioFormat.ENCODING_PCM_16BIT);
				// 버퍼 크기가 가져올 수 없습니다. 샘플링 레이트 등의 설정을 단말기가 지원하지 않을 수있다.
				if (minBufferSize < 0) {
					if (mHandler != null) {
						mHandler.sendEmptyMessage(MSG_ERROR_GET_MIN_BUFFERSIZE);
					}
					return;
				}
				// getMinBufferSizeで取得した値の場合
				// "W/AudioFlinger(75): RecordThread: buffer overflow"が発生するようであるため、少し大きめの値にしている
				//AudioRecord audioRecord = new AudioRecord(MediaRecorder.AudioSource.MIC, mSampleRate, AudioFormat.CHANNEL_IN_MONO, AudioFormat.ENCODING_PCM_16BIT, minBufferSize * 2);
				AudioRecord audioRecord = new AudioRecord(MediaRecorder.AudioSource.VOICE_CALL, mSampleRate, AudioFormat.CHANNEL_IN_MONO, AudioFormat.ENCODING_PCM_16BIT, minBufferSize * 2);

				// PCM buffer size (5sec)
				short[] buffer = new short[mSampleRate * (16 / 8) * 1 * 5]; // SampleRate[Hz] * 16bit * Mono * 5sec
				byte[] mp3buffer = new byte[(int) (7200 + buffer.length * 2 * 1.25)];

				FileOutputStream output = null;
				try {
					output = new FileOutputStream(new File(mFilePath));
				} catch (FileNotFoundException e) {
					// 파일을 생성 할 수없는
					if (mHandler != null) {
						mHandler.sendEmptyMessage(MSG_ERROR_CREATE_FILE);
					}
					return;
				}

				// Lame init
				SimpleLame.init(mSampleRate, 1, mSampleRate, 32);

				mIsRecording = true; // 녹음 시작 신고
				try {
					try {
						audioRecord.startRecording(); // 녹음을 시작하려면
					} catch (IllegalStateException e) {
						// 녹음 시작 실패
						if (mHandler != null) {
							mHandler.sendEmptyMessage(MSG_ERROR_REC_START);
						}
						return;
					}

					try {
						// 녹음을 시작했다
						if (mHandler != null) {
							mHandler.sendEmptyMessage(MSG_REC_STARTED);
						}

						int readSize = 0;
						while (mIsRecording) {
							readSize = audioRecord.read(buffer, 0, minBufferSize);
							if (readSize < 0) {
								// 녹음 수 없다
								if (mHandler != null) {
									mHandler.sendEmptyMessage(MSG_ERROR_AUDIO_RECORD);
								}
								break;
							}
							// 데이터를로드 할 수없는 경우는 아무것도하지
							else if (readSize == 0) {
								;
							}
							// 데이터가 들어있는 경우
							else {
								int encResult = SimpleLame.encode(buffer, buffer, readSize, mp3buffer);
								
								if (encResult < 0) {
									// 인코딩 실패
									if (mHandler != null) {
										mHandler.sendEmptyMessage(MSG_ERROR_AUDIO_ENCODE);
									}
									break;
								}
								if (encResult != 0) {
									try {
										output.write(mp3buffer, 0, encResult);
									} catch (IOException e) {
										// 파일 내보내기 실패
										if (mHandler != null) {
											mHandler.sendEmptyMessage(MSG_ERROR_WRITE_FILE);
										}
										break;
									}
								}
							}
						}

						int flushResult = SimpleLame.flush(mp3buffer);
						if (flushResult < 0) {
							// 인코딩 실패
							if (mHandler != null) {
								mHandler.sendEmptyMessage(MSG_ERROR_AUDIO_ENCODE);
							}
						}
						if (flushResult != 0) {
							try {
								output.write(mp3buffer, 0, flushResult);
							} catch (IOException e) {
								// 파일 내보내기 실패
								if (mHandler != null) {
									mHandler.sendEmptyMessage(MSG_ERROR_WRITE_FILE);
								}
							}
						}

						try {
							output.close();
						} catch (IOException e) {
							// 파일 닫기 실패
							if (mHandler != null) {
								mHandler.sendEmptyMessage(MSG_ERROR_CLOSE_FILE);
							}
						}
					} finally {
						audioRecord.stop(); // 녹음을 중지하려면
						audioRecord.release();
					}
				} finally {
					SimpleLame.close();
					mIsRecording = false; // 녹음 시작 플래그를 낮추는
				}

				// 녹음이 끝난
				if (mHandler != null) {
					mHandler.sendEmptyMessage(MSG_REC_STOPPED);
				}
			}
		}.start();
	}

	/**
	 * 녹음을 중지하려면
	 */
	public void stop() {
		mIsRecording = false;
	}

	/**
	 * 녹음 중 하나를 얻을
	 * 
	 * @return true의 경우 녹음 중, 그렇지 않으면 false
	 */
	public boolean isRecording() {
		return mIsRecording;
	}

	/**
	 * 녹음 상태 변화를 알려주기 핸들러 설정
	 * 
	 * @param handler
	 *            녹음 상태 변화를 알려주기 핸들러
	 * 
	 * @see RecMicToMp3#MSG_REC_STARTED
	 * @see RecMicToMp3#MSG_REC_STOPPED
	 * @see RecMicToMp3#MSG_ERROR_GET_MIN_BUFFERSIZE
	 * @see RecMicToMp3#MSG_ERROR_CREATE_FILE
	 * @see RecMicToMp3#MSG_ERROR_REC_START
	 * @see RecMicToMp3#MSG_ERROR_AUDIO_RECORD
	 * @see RecMicToMp3#MSG_ERROR_AUDIO_ENCODE
	 * @see RecMicToMp3#MSG_ERROR_WRITE_FILE
	 * @see RecMicToMp3#MSG_ERROR_CLOSE_FILE
	 */
	public void setHandle(Handler handler) {
		this.mHandler = handler;
	}
}
